package yartiss.engine.simulation.partition;

import java.util.List;

import yartiss.engine.simulation.Processor;
import yartiss.engine.simulation.system.ITask;

/**
 * Real-time partitioning manager which places the tasks dynamically according
 * to the active tasks and the available processors
 * 
 * @author Manar Qamhieh
 * 
 */
public class DynamicPartitionManager implements IPartitionManager {

	@Override
	public void partition(Processor[] processors, List<ITask> activeTasks) {

		initializeProcessors(processors);

		for (ITask activeTask : activeTasks) {
			if(!hasAvailableProcessors(processors)) return;
			if(continueExecution(processors, activeTask)) continue;
			put(processors, activeTask);
		}

	}

	/**
	 * Checks if the task has been already running on one of the processors, and
	 * allows it to continue its execution on the same processor. If the current
	 * processor is already occupied by another task, a swap will be performed
	 * (an exception will be fired in the case of swap failure).
	 * 
	 * @param processors
	 *            list of all processors in the system
	 * @param task
	 *            current task to be placed
	 * @return true if the task continues its execution on the same processor,
	 *         false otherwise
	 */
	private boolean continueExecution(Processor[] processors, ITask task) {
		for (Processor processor : processors) {
			if (processor.getCurrentTask() == null)
				continue;
			if (processor.getCurrentTask().equals(task)
					&& processor.getNextTask() == null) {
				processor.setNextTask(task);
				return true;
			} else if (processor.getCurrentTask().equals(task)
					&& processor.getNextTask() != null) {
				if (put(processors, processor.getNextTask())) {
					processor.setNextTask(task);
					return true;
				} else
					throw new IllegalArgumentException(
							"No available processor to swap the tasks");
			}
		}
		return false;
	}

	/**
	 * Checks if there exists at least one available processor
	 * 
	 * @param processors
	 *            list of all available processors in the system
	 * @return true if there is at least one available processor, false
	 *         otherwise
	 */
	private boolean hasAvailableProcessors(Processor[] processors) {
		for (Processor processor : processors) {
			if (processor.getNextTask() == null)
				return true;
		}
		return false;
	}

	/**
	 * Places a task on the first available processor
	 * 
	 * @param processors
	 *            list of all processors in the system
	 * @param task
	 *            task to be placed
	 * @return true if there exist an available processor for the task to be
	 *         placed on, false otherwise
	 */
	private boolean put(Processor[] processors, ITask task) {
		for (Processor processor : processors) {
			if (processor.getNextTask() == null) {
				processor.setNextTask(task);
				return true;
			}
		}
		return false;
	}

	/**
	 * sets the next task on all of the processors to null
	 * 
	 * @param processors
	 *            list of all processors
	 */
	private void initializeProcessors(Processor[] processors) {
		for (Processor processor : processors) {
			processor.setNextTask(null);
		}
	}

}
